package etcd

import (
	"context"
	"fmt"
	"gitee.com/justin0218/store"
	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/mvcc/mvccpb"
	"google.golang.org/grpc"
	"google.golang.org/grpc/balancer/roundrobin"
	"google.golang.org/grpc/resolver"
	"strings"
)

// etcdResolver 解析struct
type etcdResolver struct {
	rawAddr string
	cc      resolver.ClientConn
	client  *clientv3.Client
	Conf    store.Config
	Log     store.Log
}

func Discovery(serverName string) *grpc.ClientConn {
	conf := new(store.Config)
	etcdSvr, err := NewService()
	if err != nil {
		panic(err)
	}
	r := &etcdResolver{rawAddr: conf.Get().Etcd.Addr, client: etcdSvr.client}
	resolver.Register(r)
	conn, err := grpc.Dial(r.Scheme()+"://"+conf.Get().Etcd.Schema+"/"+serverName, grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, roundrobin.Name)), grpc.WithInsecure())
	if err != nil {
		panic(err)
	}
	return conn
}

// Build 构建etcd client
func (r *etcdResolver) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
	r.cc = cc
	go r.watch("/" + target.Scheme + "/" + target.Endpoint + "/")
	return r, nil
}

// Scheme etcd resolve scheme
func (r *etcdResolver) Scheme() string {
	return r.Conf.Get().Etcd.Schema
}

// ResolveNow
func (r *etcdResolver) ResolveNow(rn resolver.ResolveNowOptions) {

}

// Close closes the resolver
func (r *etcdResolver) Close() {

}

// watch 监听resolve列表变化
func (r *etcdResolver) watch(keyPrefix string) {
	var addrList []resolver.Address
	getResp, err := r.client.Get(context.Background(), keyPrefix, clientv3.WithPrefix())
	if err != nil {
		r.Log.Get().Error("discovery etcdResolver watch err:%v", err)
		return
	} else {
		for i := range getResp.Kvs {
			addrList = append(addrList, resolver.Address{Addr: strings.TrimPrefix(string(getResp.Kvs[i].Key), keyPrefix)})
		}
	}
	// 新版本etcd去除了NewAddress方法 以UpdateState代替
	r.cc.UpdateState(resolver.State{Addresses: addrList})
	rch := r.client.Watch(context.Background(), keyPrefix, clientv3.WithPrefix())
	for n := range rch {
		for _, ev := range n.Events {
			addr := strings.TrimPrefix(string(ev.Kv.Key), keyPrefix)
			switch ev.Type {
			case mvccpb.PUT:
				if !exist(addrList, addr) {
					addrList = append(addrList, resolver.Address{Addr: addr})
					r.cc.UpdateState(resolver.State{Addresses: addrList})
				}
				//fmt.Println("地址变化", addrList)
			case mvccpb.DELETE:
				if s, ok := remove(addrList, addr); ok {
					addrList = s
					r.cc.UpdateState(resolver.State{Addresses: addrList})
				}
				//fmt.Println("地址变化被删除", addrList)
			}
		}
	}
}

// exist 判断resolve address是否存在
func exist(l []resolver.Address, addr string) bool {
	for i := range l {
		if l[i].Addr == addr {
			return true
		}
	}
	return false
}

// remove 从resolver列表移除
func remove(s []resolver.Address, addr string) ([]resolver.Address, bool) {
	for i := range s {
		if s[i].Addr == addr {
			s[i] = s[len(s)-1]
			return s[:len(s)-1], true
		}
	}
	return nil, false
}

//type ClientMon struct {
//	client     *clientv3.Client
//	serverList map[string]string
//	lock       sync.Mutex
//}

//// 初始化server端
//func NewClientMon(addr []string) (*ClientMon, error) {
//	conf := clientv3.Config{
//		Endpoints:   addr,
//		DialTimeout: 5 * time.Second,
//	}
//	if client, err := clientv3.New(conf); err == nil {
//		return &ClientMon{
//			client:     client,
//			serverList: make(map[string]string),
//		}, nil
//	} else {
//		return nil, err
//	}
//}

//// 获取prefix目录下所有内容，并返回
//func (this *ClientMon) GetService(prefix string) ([]string, error) {
//	resp, err := this.client.Get(context.Background(), prefix, clientv3.WithPrefix())
//	if err != nil {
//		return nil, err
//	}
//	// resp是从指定prefix服务下get回的value，extractAddrs将value内容存到list
//	addrs := this.extractAddrs(resp)
//	go this.watcher(prefix)
//	return addrs, nil
//}
//
//// 将获取到的prefix目录下所有内容存入list并返回
//func (this *ClientMon) extractAddrs(resp *clientv3.GetResponse) []string {
//	addrs := make([]string, 0)
//	if resp == nil || resp.Kvs == nil {
//		return addrs
//	}
//	for i := range resp.Kvs {
//		if v := resp.Kvs[i].Value; v != nil {
//			this.SetServiceList(string(resp.Kvs[i].Key), string(resp.Kvs[i].Value))
//			addrs = append(addrs, string(v))
//		}
//	}
//	return addrs
//}
//
//// watch负责将监听到的put、delete请求存放到指定list
//func (this *ClientMon) watcher(prefix string) {
//
//	rch := this.client.Watch(context.Background(), prefix, clientv3.WithPrefix())
//	for wresp := range rch {
//		for _, ev := range wresp.Events {
//			fmt.Println("触发....", ev.Type.String())
//			switch ev.Type {
//			case mvccpb.PUT:
//				this.SetServiceList(string(ev.Kv.Key), string(ev.Kv.Value))
//				fmt.Println("最新地址列表", this.serverList)
//			case mvccpb.DELETE:
//				this.DelServiceList(string(ev.Kv.Key))
//				fmt.Println("最新地址列表", this.serverList)
//			}
//		}
//	}
//
//}
//
//func (this *ClientMon) SetServiceList(key, val string) {
//	this.lock.Lock()
//	defer this.lock.Unlock()
//	this.serverList[key] = string(val)
//	etcd.log.Println("set data key :", key, "val:", val)
//}
//
//func (this *ClientMon) DelServiceList(key string) {
//	this.lock.Lock()
//	defer this.lock.Unlock()
//	delete(this.serverList, key)
//	etcd.log.Println("del data key:", key)
//}
